﻿using System;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Windows.Forms;
using M = Clock.Utils.MsgBox.MessageBoxInterop;

namespace Clock.Utils.MsgBox
{
    public static class MessageBoxCenteringService
    {
        [ThreadStatic]
        private static IntPtr _hook;

        [ThreadStatic]
        private static M.HookProc _hookProc;

        public static IDisposable Initialize()
        {
            if (_hook == IntPtr.Zero)
            {
                _hookProc = HandleMessage;
                _hook = M.SetWindowsHookEx(M.HookType.WH_CALLWNDPROCRET, _hookProc, IntPtr.Zero, M.GetCurrentThreadId());
            }

            return new HookDispose(_hook);
        }

        private static IntPtr HandleMessage(int code, IntPtr wParam, IntPtr lParam)
        {
            if (code >= 0)
            {
                ProcessMessage(lParam);
            }

            return M.CallNextHookEx(_hook, code, wParam, lParam);
        }

        private static void ProcessMessage(IntPtr param)
        {
            var message = Marshal.PtrToStructure<M.Msg>(param);

            if (message.Message != 0x0018)
            {
                return;
            }

            var box = message.Hwnd;
            var parent = M.GetParent(box);

            if (parent == IntPtr.Zero)
            {
                return;
            }

            var className = new StringBuilder(7);

            M.GetClassName(box, className, className.Capacity);

            if (!className.ToString().Equals("#32770"))
            {
                return;
            }

            if (!M.GetWindowRect(box, out var boxRect))
            {
                return;
            }

            if (!M.GetWindowRect(parent, out var parentRect))
            {
                return;
            }

            var width = boxRect.Right - boxRect.Left;
            var height = boxRect.Bottom - boxRect.Top;
            var left = (parentRect.Left + parentRect.Right) / 2 - width / 2;
            var top = (parentRect.Top + parentRect.Bottom) / 2 - height / 2;
            var workRect = Screen.GetWorkingArea(new Point(boxRect.Left, boxRect.Top));

            left = Math.Max(0, Math.Min(left, workRect.X + workRect.Width - width));
            top = Math.Max(0, Math.Min(top, workRect.Y + workRect.Height - height));

            const M.SetWindowPosFlags commonFlags =
                M.SetWindowPosFlags.SynchronousWindowPosition |
                M.SetWindowPosFlags.IgnoreResize |
                M.SetWindowPosFlags.DoNotActivate |
                M.SetWindowPosFlags.DoNotChangeOwnerZOrder |
                M.SetWindowPosFlags.IgnoreZOrder;

            M.SetWindowPos(box, IntPtr.Zero, left, top, width, height, commonFlags);
        }

        private class HookDispose : IDisposable
        {
            private IntPtr _hook;
            private int _isDisposed = 0;

            public HookDispose(IntPtr hook)
            {
                _hook = hook;
            }

            public void Dispose()
            {
                if (Interlocked.CompareExchange(ref _isDisposed, 1, 0) == 0)
                {
                    if (_hook != IntPtr.Zero)
                    {
                        M.UnhookWindowsHookEx(_hook);
                    }
                }
            }
        }
    }
}